package com.geoai.uimap.utils

import com.geoai.basiclib.utils.log.MyLogUtils

class MathUtils {

    class Point() {
        var x: Double = 0.0
        var y: Double = 0.0

        constructor(x: Double, y: Double) : this() {
            this.x = x
            this.y = y
        }

        override fun toString(): String {
            return "Point{" +
                    "x=" + x +
                    ", y=" + y +
                    '}'
        }
    }


    class Circle {
        var center: Point = Point()
        var radius: Double = 0.0

        override fun toString(): String {
            return "Circle{" +
                    "center=" + center +
                    ", radius=" + radius +
                    '}'
        }
    }

    companion object {


        //通过圆心角求圆上的某一点
        @JvmStatic
        fun getPointOnCircle(circle: Circle, angle: Double): Point {
            return Point(
                circle.center.x + Math.cos(angle) * circle.radius, circle.center.y + Math.sin(
                    angle
                ) * circle.radius
            )
        }

        /**
         * 向量AB
         * */
        @JvmStatic
        fun getVector(A: Point, B: Point): Point {
            return Point(B.x - A.x, B.y - A.y)
        }

        /**
         * 两点间距离
         * */
        @JvmStatic
        fun getLen(A: Point, B: Point): Double {
            return Math.sqrt(getLen2(A, B))
        }

        /**
         * 两点间距离的平方
         * */
        @JvmStatic
        fun getLen2(A: Point, B: Point): Double {
            val AB = getVector(A, B)
            return Math.pow(AB.x, 2.0) + Math.pow(AB.y, 2.0)
        }

        /**
         * 求两个圆的公切线
         * https://www.liangzl.com/get-article-detail-179947.html
         * 0、两个圆内含，没有公共点，没有公切线
         * 1、两圆内切，有一个条公切线
         * Int.MAX_VALUE、两圆完全重合，有无数条公切线
         * 2、两圆相交。有2条公切线
         * 3、两圆外切，有3条公切线
         * 4、两圆相离，有4条公切线
         *
         * */
        @JvmStatic
        fun getTangents(A: Circle, B: Circle, a: MutableList<Point>, b: MutableList<Point>): Int {
            var cnt = 0
            // 以A为半径更大的那个圆进行计算
            if (A.radius < B.radius) return getTangents(B, A, b, a)
            val d2 = getLen2(A.center, B.center)        // 圆心距平方
            val rdiff = A.radius - B.radius            // 半径差
            val rsum = A.radius + B.radius                //半径和
            MyLogUtils.d("d2=$d2, rdiff2=${rdiff * rdiff}, rsum2=${rsum * rsum} ")
            if (d2 < rdiff * rdiff) return 0                    // 情况1，内含,没有公切线
            val AB = getVector(A.center, B.center)        // 向量AB，其模对应圆心距
            val base = Math.atan2(AB.y, AB.x)            // 求出向量AB对应的极角
            if (d2 == 0.0 && A.radius == B.radius) return Int.MAX_VALUE    // 情况3，两个圆重合，无限多切线
            if (d2 == rdiff * rdiff) {                            // 情况2，内切，有一条公切线
                a.add(getPointOnCircle(A, base))
                b.add(getPointOnCircle(B, base))
                cnt = cnt.inc()
                return cnt
            }
            // 求外公切线
            val ang = Math.acos((A.radius - B.radius) / Math.sqrt(d2)) //求阿尔法
            // 两条外公切线
            a.add(getPointOnCircle(A, base + ang))
            b.add(getPointOnCircle(B, base + ang))
            cnt = cnt.inc()
            a.add(getPointOnCircle(A, base - ang))
            b.add(getPointOnCircle(B, base - ang))
            cnt = cnt.inc()
            if (d2 == rsum * rsum) {  // 情况5，外切，if里面求出内公切线
                a.add(getPointOnCircle(A, base))
                b.add(getPointOnCircle(B, Math.PI + base))
                cnt = cnt.inc()
            } else if (d2 > rsum * rsum) {    //情况6，相离，再求出内公切线
                val ang = Math.acos((A.radius + B.radius) / Math.sqrt(d2))
                a.add(getPointOnCircle(A, base + ang))
                b.add(getPointOnCircle(B, Math.PI + base + ang))
                cnt = cnt.inc()
                a.add(getPointOnCircle(A, base - ang))
                b.add(getPointOnCircle(B, Math.PI + base - ang))
                cnt = cnt.inc()
            }
            // 此时，d2 < rsum * rsum 代表情况 4 只有两条外公切线
            return cnt
        }

        /**
         *  二元一次方程组求解       求y
         *  y = k(x-a)+b
         *
         * */
        @JvmStatic
        fun xyPow1(k: Double, a: Double, b: Double, x: Double): Double {
            return k * (x - a) + b
        }

        /**
         *  一元二次方程组求解
         *  ax^2 + bx +c = 0
         *
         *
         *  x=(-b +/- √(b^2-4ac))/2a
         *
         * */
        @JvmStatic
        fun xPow2(a: Double, b: Double, c: Double): MutableList<Double> {
            val result = ArrayList<Double>()

            if (a == 0.0 && b == 0.0) {
                return result
            }

            if (a == 0.0) {
                result.add(-c / b)
                return result
            }

            result.add((-b + Math.sqrt(Math.pow(b, 2.0) - 4 * a * c)) / (2 * a))
            result.add((-b - Math.sqrt(Math.pow(b, 2.0) - 4 * a * c)) / (2 * a))
            return result
        }

        /**
         * 求直线过圆心在圆上的两点
         *
         * 圆心   centerX,centerY
         * 半径   radius
         * 圆    (x-centerX)^2 + (y-centerY)^2 = radius^2
         * 直线过圆心    y = k(x-centerX)+centerY
         * 直线与圆交点  x1 = (√(radius^2/(k^2+1)))+centerX
         *              y1 = k(x1-centerX)+centerY
         *              x2 = (-√(radius^2/(k^2+1)))+centerX
         *              y2 = k(x2-centerX)+centerY
         *
         * */
        @JvmStatic
        fun lineOnCircle(center: Point, radius: Double, k: Double?): ArrayList<Point> {

//            Timber.d("center=$center， radius=$radius， k=$k")

            val result = ArrayList<Point>()

            val circle1X: Double
            val circle1Y: Double
            val circle2X: Double
            val circle2Y: Double
            if (k == null || k.isNaN() || k.isInfinite()) {
                MyLogUtils.d("k.isInfinite()")
                circle1X = center.x
                circle1Y = center.y + radius
                circle2X = center.x
                circle2Y = center.y - radius
            } else {
                circle1X = Math.sqrt(Math.pow(radius, 2.0) / (Math.pow(k, 2.0) + 1)) + center.x
                circle1Y = k * (circle1X - center.x) + center.y
                circle2X = -Math.sqrt(Math.pow(radius, 2.0) / (Math.pow(k, 2.0) + 1)) + center.x
                circle2Y = k * (circle2X - center.x) + center.y
            }

            result.add(Point(circle1X, circle1Y))
            result.add(Point(circle2X, circle2Y))

            return result
        }

        /**
         * 与线上一点相切的两个园求解
         * */
        @JvmStatic
        fun getCircles(
            point1: Point,
            point2: Point,
            radius: Double
        ): ArrayList<Point> {
            val kP1P2 = (point2.y - point1.y) / (point2.x - point1.x)
            val kVertP1P2 = -1 / kP1P2
            return lineOnCircle(point1, radius, kVertP1P2)

        }

        @JvmStatic
        fun calcQieDian(
            centerX: Double,
            centerY: Double,
            outsideX: Double,
            outsideY: Double,
            radius: Double,
            flag: Boolean
        ): Point? {
            val center =
                Point(centerX, centerY)
            val outside =
                Point(outsideX, outsideY)
            val qiedian =
                calcQieDian(center, outside, radius, flag)
            println("center=$center， outside=$outside， qiedian=$qiedian")
            return qiedian
        }


        //https://blog.csdn.net/hekejun19861107/article/details/12944117
        @JvmStatic
        fun calcQieDian(
            ptCenter: Point,
            ptOutside: Point,
            dbRadious: Double,
            flag: Boolean
        ): Point {
            val E = Point()
            val F = Point()
            val G = Point()
            val H = Point()
            //1. 坐标平移到圆心ptCenter处,求园外点的新坐标E
            E.x = ptOutside.x - ptCenter.x
            E.y = ptOutside.y - ptCenter.y //平移变换到E

            //2. 求园与OE的交点坐标F, 相当于E的缩放变换
            val t = dbRadious / Math.sqrt(E.x * E.x + E.y * E.y) //得到缩放比例
            F.x = E.x * t
            F.y = E.y * t //缩放变换到F

            //3. 将E旋转变换角度a到切点G，其中cos(a)=r/OF=t, 所以a=arccos(t);
            val a: Double
            a = if (flag) {
                Math.acos(t) //得到旋转角度
            } else {
                -Math.acos(t) //得到旋转角度
            }
            G.x = F.x * Math.cos(a) - F.y * Math.sin(a)
            G.y = F.x * Math.sin(a) + F.y * Math.cos(a) //旋转变换到G

            //4. 将G平移到原来的坐标下得到新坐标H
            H.x = G.x + ptCenter.x
            H.y = G.y + ptCenter.y //平移变换到H

            //5. 返回H
            return H
            //6. 实际应用过程中，只要一个中间变量E,其他F,G,H可以不用。
        }


        /**
         * 计算三点的顺逆时针方向
         *
         * @param
         * @return 1 顺时针
         * 2 逆时针
         */
        @JvmStatic
        fun getClockWise(
            centerCircleX: Double,
            centerCircleY: Double,
            point1X: Double,
            point1Y: Double,
            point2X: Double,
            point2Y: Double
        ): Int {
            //固定翼转弯方向
            //分别输入A，B，C三点的坐标
            val ans =
                (point1X - centerCircleX) * (point2Y - centerCircleY) - (point1Y - centerCircleY) * (point2X - centerCircleX) //表示向量AB与AC的叉积的结果
            if (ans > 0) {
                //"逆时针"
                return 2
            } else if (ans < 0) {
                //"顺时针"
                return 1
            }
            return -1
        }

        /**
         * 计算三点的顺逆时针方向
         *
         * @param
         * @return 1 顺时针
         * 2 逆时针
         */
//        @JvmStatic
//        fun getClockWise(
//            centerCircle: Point,
//            point1: Point,
//            point2: Point
//        ): Int {
//            return getClockWise(centerCircle.x,centerCircle.y,point1.x,point1.y,point2.x,point2.y);
//        }

        /**
         * 计算三点的顺逆时针方向
         *
         * @param
         * @return 1 顺时针
         * 2 逆时针
         */
        @JvmStatic
        fun getClockWise(
            centerCircle: Point,
            point1: Point,
            point2: Point
        ): Int {
            //两点的直线方程 kx + b = y
            val kP1P2 = (point2.y - point1.y) / (point2.x - point1.x)
            var isUpLine = false    //是否在线的上方
            var ret = 1;

            MyLogUtils.d("centerCircle=${centerCircle}")
            MyLogUtils.d("point1=${point1}")
            MyLogUtils.d("point2=${point2}")
            MyLogUtils.d("kP1P2=${kP1P2}")
            if (kP1P2.isNaN() || kP1P2.isInfinite()) {   // x1 == x2
                if (centerCircle.x < point1.x) {         //
                    ret = if (point1.y < point2.y) 2 else 1
                } else {
                    ret = if (point1.y < point2.y) 1 else 2
                }
            } else {
                val b = point1.y - kP1P2 * point1.x
                val y = kP1P2 * centerCircle.x + b
                isUpLine = centerCircle.y > y
                if (isUpLine) {
                    ret = if (point1.x < point2.x) 2 else 1
                } else {
                    ret = if (point1.x < point2.x) 1 else 2
                }
            }

            return ret
        }

    }

}

/**
 * 左移shlSize位，保留containsize位
 * */
fun Int.shl(shlSize: Int, containsize: Int): Int {
    val shlValut = this.shr(shlSize)
    val ffValue: Int = 0xffffffff.toInt()
    val shrffValue = ffValue.shl(containsize)
    val invshrffValue = shrffValue.inv()
    val ret = shlValut.and(invshrffValue)

    return ret
}